#!/usr/bin/env python

import args_parser
import os.path
import time
import lts
import pdb
import sys
import os

from heuristics import *

# Tipos de sincronizacion.
SYNC_EMPTY		=	0
SYNC_PARTIAL	=	1
SYNC_FULL		=	2

class DADDYError (Exception):
	"""
		Clase de excepcion para este modulo.
	"""
	pass

def str_time (t):
	if t > 60.0: t = t/60.0
	else: return str(t)+" seconds."
	if t > 60.0: t = t/60.0
	else: return str(t)+" minutes."
	return str(t)+" hours."

def share_events (s1, s2):
	"""
		Esta funcion dice si dos sistemas comparten eventos observables.
	"""
	e1 = s1.get_event_set('o')
	e2 = s2.get_event_set('o')
	if e1.intersection(e2): return True
	else: return False

def check_sync_type (system, sync_set):
	# Dice si hay al menos dos subsistemas que tengan eventos en comun.
	some_share = False
	# Almacena la interseccion de los eventos observables de todos los subsistemas.
	inter_set = system[0].get_event_set('o')
	for subsystem in system:
		inter_set = inter_set.intersection(subsystem.get_event_set('o'))
		if not some_share:
			for s in system:
				if s != subsystem and share_events(subsystem, s):
					some_share = True
					break
	if not sync_set or (not inter_set and not some_share):
		return SYNC_EMPTY
	elif inter_set.issubset(sync_set):
		return SYNC_FULL
	else:
		return SYNC_PARTIAL

def show_nondiag_info (subsystem, diag_res, graphic):
	subsystem_tp = subsystem.get_twinplant()
	map_dict = {subsystem:lts.to_file(subsystem)}
	map_dict[subsystem_tp] = lts.to_file(subsystem_tp)
	print "\nThe subsystem",subsystem.name,"has the following non-diagnosable faults:"
	for (f,path) in diag_res['fault_list']:
		print "* Fault:", f
		print "- Undecidible path:"
		print "\t",lts.path_to_string((path,subsystem_tp), map_dict)
		subsys_paths = lts.tp_to_sys_path((path,subsystem), map_dict)
		print "- Undesirable pair:"
		print '\t', subsys_paths[0]
		print '\t', subsys_paths[1]
	if graphic:
		lts.graph(subsystem.name)
		lts.graph(subsystem_tp.name)

def _mode_0 (parsed_args):
	system = parsed_args["system"]
	fault_set = parsed_args["fault_set"]
	sync_set = parsed_args["sync_set"]
	graphic = parsed_args["graphic"]
	heuristic_level = parsed_args["heuristic_level"]
	take_time = parsed_args["time"]
	verbose = parsed_args["verbose"]
	if verbose:
		print "\nINFORMATION"
		print "-----------"
	# Condicion para el ciclo pricipal.
	decide_result = False
	if verbose: iter = 1 # Cuenta las iteraciones para el modo verbose=True.
	while not decide_result:
		# Chequeo el tipo de sincronizacion.
		sync_type = check_sync_type(system, sync_set)
		# Numero de iteracion.
		if verbose: print "\nIteration:", iter
		# Cantidad de subsistemas.
		if verbose: print "\nNumber of subsystems: ", len(system)
		# Tipo de sincronizacion en esta iteracion.
		if verbose:
			if sync_type == SYNC_EMPTY: print "Synchronization type:  EMPTY"
			elif sync_type == SYNC_PARTIAL:	print "Synchronization type:  PARTIAL"
			else: print "Synchronization type:  FULL"
		if verbose: iter += 1
		# Dice si todos los subsistemas son diagnosticables.
		all_subsystems_diag = True
		# Almacena los resultados de los subsistemas no diagnosticables.
		all_nondiag_subsystems = []
		# Chequeo la diagnosticabilidad de los subsistemas.
		for subsystem in system:
			subsystem.isdiag = None
			if verbose: print "\nChecking diagnosability for:", subsystem.name
			if take_time: t1 = time.time()
			diag_res = subsystem.is_diagnosable(fault_set,verbose)
			if take_time: t2 = time.time()
			if verbose:
				if diag_res['is_diag']: print "It's diagnosable!"
				else: print "It's non-diagnosable!"
				if take_time: print "It takes",str_time(t2-t1)
			subsystem.isdiag = diag_res['is_diag']
			all_subsystems_diag &= diag_res['is_diag']
			if not diag_res['is_diag']:
				all_nondiag_subsystems.append((subsystem,diag_res))
		# Chequeo si puedo decidir la diagnosticabilidad del sistema.
		if len(system) == 1: # Si hay un subsistema es facil.
			decide_result = True
			if all_subsystems_diag:
				print "\nRESULT"
				print "------"
				print "\nThe system is diagnosable."
			else:
				(subsystem,diag_res) = all_nondiag_subsystems[0]
				print "\nRESULT"
				print "------"
				print "\nThe system is non-diagnosable."
				show_nondiag_info(subsystem, diag_res, graphic)
		elif (sync_type==SYNC_EMPTY) and not all_subsystems_diag:
			# El sistema no es diagnosticable.
			decide_result = True
			print "\nRESULT"
			print "------"
			print "\nThe system is non-diagnosable because the synchronization type"
			print "is EMPTY and there exist at least one non-diagnosable subsystem."
			for (subsystem,diag_res) in all_nondiag_subsystems:
				show_nondiag_info(subsystem, diag_res, graphic)
				print ''
		else:
			# No puedo decidir y hay mas de un sistema. Tengo que sincronizar.
			system = synchronize(system, sync_set, heuristic_level, verbose)

if __name__ == '__main__':
	# Parseo los argumentos de entrada.
	parsed_args = args_parser.parse(sys.argv[1:],2,8)
	take_time = parsed_args["time"]
	mode = parsed_args["mode"]
	# Para contar el tiempo global.
	if take_time: gt1 = time.time()
	# Ejecuto el modo indicado.
	if mode == 0:
		_mode_0(parsed_args)
	else:
		# En el futuro habra mas modos.
		pass
	# Para contar el tiempo global.
	if take_time: gt2 = time.time()
	if take_time: print "\nTotal time:",str_time(gt2-gt1),"\n"
